package com.qiniu.githubdata.job;//package com.qiniu.race.job;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qiniu.githubdata.entity.Contribution;
import com.qiniu.githubdata.entity.DeveloperFollower;
import com.qiniu.githubdata.service.DeveloperService;
import com.qiniu.githubdata.service.RepositoryService;
//import com.qiniu.githubdata.service.impl.RepositoryServiceImpl;
import com.qiniu.githubdata.service.TagService;
import org.jetbrains.annotations.NotNull;
import org.kohsuke.github.*;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qiniu.githubdata.entity.Developer;
import com.qiniu.githubdata.entity.Repository;
import com.qiniu.githubdata.mapper.RepositoryMapper;
import com.qiniu.githubdata.mapper.DeveloperMapper;
import com.qiniu.githubdata.mapper.ContributionMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class GitHubRepoFetcher {
    @Autowired
    private  RepositoryMapper repositoryMapper;
    @Resource
    private TagService tagService;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private DeveloperService developerService;
    @Autowired
    private  DeveloperMapper developerMapper;
    @Autowired
    private  ContributionMapper contributionMapper;
    GitHub github;

    {
        try {
            github=new GitHubBuilder().
                    withOAuthToken("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").build();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //获取github repo
    public  GHRepository getRepo(String owner, String repo) throws IOException {
         GHRepository ghRepository = github.getRepository(owner + "/" + repo);
        System.out.println(ghRepository.getFullName());
         return ghRepository;
    }
    public  Map<String, GHRepository> getReposByOwner(String owner) throws IOException {
        long startTime = System.currentTimeMillis();
        Map<String, GHRepository> repos = github.getUser(owner).getRepositories();
        long endTime = System.currentTimeMillis();
        System.out.println("get repos time: " + (endTime - startTime) + "get repos count: "+repos.size());
        return repos;
    }

    public Repository convertGHRepoToRepo(GHRepository ghRepository) throws IOException {
        Repository repository = new Repository();
        repository.setId(ghRepository.getId());
        repository.setName(ghRepository.getName());
        repository.setOwner(ghRepository.getOwnerName());
        repository.setDescription(ghRepository.getDescription());
        repository.setStars(ghRepository.getStargazersCount());
        repository.setForks(ghRepository.getForksCount());
        repository.setIssues(ghRepository.getOpenIssueCount());
        repository.setLanguage(ghRepository.getLanguage());
        double importance = repositoryService.getImportance(repository);
        repository.setImportance(importance);
        return repository;
    }
    public  void storeRepoData(GHRepository repo) throws IOException {
        Repository repository = new Repository();
        String fullName = repo.getFullName();
        String repoName = repo.getName();
        String owner = repo.getOwnerName();
        //是否存在
        boolean exist = repositoryMapper.selectCount(
                new LambdaQueryWrapper<Repository>().eq(Repository::getName, repoName)
                .eq(Repository::getOwner,owner)) > 0;
        PagedIterable<GHTag> ghTags = repo.listTags();
        for (GHTag ghTag : ghTags) {
            System.out.println(ghTag.getName());
        }
        if (!exist) {
            repository.setId(repo.getId());
            repository.setName(repo.getName());
            repository.setOwner(repo.getOwnerName());
            repository.setDescription(repo.getDescription());
            repository.setStars(repo.getStargazersCount());
            repository.setForks(repo.getForksCount());
            repository.setIssues(repo.getOpenIssueCount());
            repository.setLanguage(repo.getLanguage());
            double importance = repositoryService.getImportance(repository);
            repository.setImportance(importance);
            repositoryService.save(repository);
        }else {
            QueryWrapper<Repository> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("name", repoName);
            queryWrapper.eq("owner", owner);
            repository.setStars(repo.getStargazersCount());
            repositoryMapper.update(repository, queryWrapper);
        }
    }
    public List<Contribution> getContributions(GHRepository repo) throws IOException {
        // 获取贡献者
        List<Contribution> contributionList = contributionMapper.selectList(new LambdaQueryWrapper<Contribution>().
                eq(Contribution::getRepositoryId, repo.getId()));
        for(GHRepository.Contributor contributor:repo.listContributors().toList()) {
            int contributions = contributor.getContributions();

            long id=contributor.getId();


            Developer developer = developerMapper.selectById(id);
            String login=developer==null?contributor.getLogin():developer.getLogin();
            if (contributions < 5) {
                //结束for循环
                break;
            }
            //打印重要信息
            System.out.println(contributor.getLogin() + " " + contributions);
            long startTime = System.currentTimeMillis();
            int issueCount = repo.queryIssues().creator(login).list().asList().size();

//            System.out.println("issue count: " + issueCount);
            //修改行数
            int totalAdditions = 0;
            int totalChanges = 0;
            // int totalChanges = 0;
            int totalCommits = 0;
            int totalDeletions = 0;
            int prCount = 0;

//            for (GHCommit commit : repo.queryCommits().author(contributor.getLogin()).list()) {
////                GHCommit.Stats stats = commit.getStats();
//
//                try {
//                    totalAdditions += commit.getLinesAdded();
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }
//                try {
//                    totalDeletions += commit.getLinesDeleted();
//                    totalChanges += commit.getLinesChanged();
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }
//            }
            try {
                 prCount=repo.getPullRequests(GHIssueState.ALL).size();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            //存储
            Contribution contribution = new Contribution();
            contribution.setDeveloperId(contributor.getId());
            contribution.setRepositoryId(repo.getId());
            contribution.setCommitCount(totalCommits);
            contribution.setLineChanges(totalChanges);
            contribution.setLineAdditions(totalAdditions);
            contribution.setLineDeletions(totalDeletions);
            contribution.setPrCount(prCount);
            contribution.setIssueCount(issueCount);
//            contribution.setContributionScore(0);
            contribution.setTotalContributions(contributor.getContributions());
            contributionList.add(contribution);
            //查找是否存在
            boolean exist = contributionMapper.selectCount(new LambdaQueryWrapper<Contribution>().
                    eq(Contribution::getDeveloperId, contributor.getId()).eq(Contribution::getRepositoryId, repo.getId())) > 0;
            if (!exist) {
                contributionMapper.insert(contribution);
            } else {
                contributionMapper.update(contribution, new LambdaQueryWrapper<Contribution>().
                        eq(Contribution::getDeveloperId, contributor.getId()).eq(Contribution::getRepositoryId, repo.getId()));
            }
            long endTime = System.currentTimeMillis();
            System.out.println("store contribution time: " + (endTime - startTime));
        }
        return contributionList;
    }
    public  void storeDeveloperData(GHUser user) throws IOException {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        QueryWrapper<Developer> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("login", user.getLogin());
        Developer one = developerService.getOne(queryWrapper);

        Map<String,GHRepository> repos = getReposByOwner(user.getLogin());
        String language = deduceUserLanguage(repos);
        Developer developer = getDeveloper(user, repos);
        if (one != null) {
            developerService.update(developer, queryWrapper);
        }else{
            developerService.save(developer);
        }
        tagService.addDeveloperLanguageTag(user.getId(), language);
        stopWatch.stop();
        System.out.println("store developer time: " + stopWatch.getTotalTimeMillis());
//        developerService.save(developer);
    }

    @NotNull
    private static Developer getDeveloper(GHUser user, Map<String, GHRepository> repos) throws IOException {
        Developer developer = new Developer();
        developer.setId(user.getId());
        developer.setLogin(user.getLogin());
        developer.setUserName(user.getName());
        String location = user.getLocation();
        developer.setNation(user.getLocation());
        developer.setNationConfidence(0);
        if(location != null){
            developer.setNationConfidence(100);
        }
        developer.setEstimated(false);
        developer.setEvaluationInfo("");
        developer.setTalentRank(-1);
//        developer.setDomains(null);
        developer.setAvatarUrl(user.getAvatarUrl());
        developer.setGithubUrl(String.valueOf(user.getUrl()));
        developer.setRepositoryCount(repos.size());
        developer.setBio(user.getBio());
        developer.setBlog(user.getBlog());
        return developer;
    }

    public  void storeRepoDataByOwner(String owner) throws IOException {
        Map<String, GHRepository> repos = getReposByOwner(owner);
        //计算时间
        long startTime = System.currentTimeMillis();
        for (GHRepository repo : repos.values()) {
            storeRepoData(repo);
            System.out.println("store repo data: " + repo.getFullName());
        }
        long endTime = System.currentTimeMillis();
        System.out.println("store repo data time: " + (endTime - startTime) + "store repo count: "+repos.size());
    }
    public String deduceUserLanguage( Map<String, GHRepository> repos) throws IOException {
        //获取用户的所有repo
//        Map<String, GHRepository> repos = null;
        Map<String,Integer> languageCount = new HashMap<>();
        //            repos = user.getRepositories();

        for(GHRepository repo:repos.values()) {
            //获取repo的语言
            String language = repo.getLanguage();

            if(language!=null){
                if(languageCount.containsKey(language)){
                    languageCount.put(language,languageCount.get(language)+1);
                }else{
                    languageCount.put(language,1);
                }
            }
        }
        //获取最多的语言
        int maxCount = 0;
        String maxLanguage = null;
        for (Map.Entry<String, Integer> entry : languageCount.entrySet()) {
            if(entry.getValue()>maxCount){
                maxCount = entry.getValue();
                maxLanguage = entry.getKey();
            }
        }
        return maxLanguage;
    }

    public void getUserFollowers(String login) throws IOException {
        GHUser user = github.getUser(login);
        for (GHUser follower : user.listFollowers()) {
            System.out.println(follower.getLogin());
        }
    }
    public void searchUser(){
        github.searchUsers().q("followers:>0").sort(GHUserSearchBuilder.Sort.FOLLOWERS).order(GHDirection.DESC).list().forEach(user -> {
//            System.out.println(user.getLogin());
            try {
                storeDeveloperData(user);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }
    @NotNull
    private Developer convertUserToDeveloper(GHUser user, Map<String, GHRepository> repos) throws IOException {
        Developer developer = new Developer();
        developer.setId(user.getId());
        developer.setLogin(user.getLogin());
        developer.setUserName(user.getName());

        String location = user.getLocation();
        if (location != null) {
            developer.setNation(location);
            developer.setNationConfidence(100);
            developer.setEstimated(false);
        } else {
            developer.setNation(null);
            developer.setNationConfidence(0);
            developer.setEstimated(true);
        }
        // ... 其他字段设置
        return developer;
    }
    private List<GHUser> getFollowers(GHUser user) throws IOException {
       return user.listFollowers().withPageSize(10).toList();
    }
    public void saveFollowersInMysql(GHUser user) throws IOException {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        List<GHUser> followers = getFollowers(user);
        for (GHUser follower : followers) {
            Developer developer = convertUserToDeveloper(follower, getReposByOwner(follower.getLogin()));
//            developerService.save(developer);
            StopWatch stopWatch1 = new StopWatch();
            stopWatch1.start();
            developerMapper.insertOrUpdateDeveloper(developer);
            developerMapper.insertFollowerIfNotExist(new DeveloperFollower(user.getId(), developer.getId()));
            stopWatch1.stop();
            System.out.println("sql follower time: " + stopWatch1.getTotalTimeMillis());
            System.out.println("store follower: " + follower.getLogin());
        }
        stopWatch.stop();
        System.out.println("store followers time: " + stopWatch.getTotalTimeMillis() + "followers count: " + followers.size());
    }
}